package FrameWork.World;

import FrameWork.World.Util.StopWatch;
import FrameWork.World.Util.TimeUtil;
import org.apache.log4j.Logger;
import java.util.*;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 掌管场景的天神
 * 一个天神与一个线程绑定，一个天神管理若干个线程
 */
public class God implements Runnable{
    private Logger logger = Logger.getLogger(God.class);
    public static final String DEFAULT_NAME = "God";
    private String name = DEFAULT_NAME;
    //绑定的线程
    private Thread bindThread;
    //该天神管理的所有场景
    private LinkedList<AbstractScene> scenes = new LinkedList<>();
    private Set<AbstractScene> sceneSet = new HashSet<>();
    //准备挂载的场景
    private ConcurrentLinkedQueue<AbstractScene> prepareScenes = new ConcurrentLinkedQueue<>();
    private ReentrantLock readWriteLock = new ReentrantLock();
    //33毫秒刷新一次
    public final static int TICK_TIME = 33;
    public final static int MIN_SLEEP_TIME = 10;
    //帧计时器
    private final StopWatch tickStopWatch = new StopWatch(TICK_TIME);
    //当前是否在运行
    private volatile AtomicBoolean running = new AtomicBoolean(false);
    //God是否被激活
    private volatile AtomicBoolean alive = new AtomicBoolean(false);

    God(){ }

    public void start(){
        if (bindThread!=null){
            bindThread.start();
        }else{
            logger.error(name+"还未绑定线程，启动失败");
        }
    }

    @Override
    public void run() {
        running.set(true);
        alive.set(true);
        while(running.get()){
            //重置时间
            long curr = TimeUtil.getCurrentTimeMill();
            tickStopWatch.reset(curr);
            //执行场景挂载
            processRegister();
            //执行场景的心跳
            Iterator<AbstractScene> iterator = scenes.iterator();
            while (iterator.hasNext()){
                AbstractScene scene = iterator.next();
                if (!scene.isRunning()){
                    //执行场景的卸载
                    iterator.remove();
                    sceneSet.remove(scene);
                    logger.debug("移除场景"+scene.getName());
                }else{
                    scene.tick(curr);
                }
            }
            //执行间隔执行睡眠
            try {
                if (!tickStopWatch.hasTimeOut()){
                    long remain = tickStopWatch.getRemainTime();
                    Thread.sleep(remain>MIN_SLEEP_TIME ? remain : MIN_SLEEP_TIME);
                }else {
                    logger.warn(getName()+"执行时间过长，执行时长为"+(TimeUtil.getCurrentTimeMill()-tickStopWatch.getLastTime()));
                    Thread.sleep(MIN_SLEEP_TIME);
                }
            } catch (InterruptedException e) {
                logger.error(Thread.currentThread(),e);
                //响应中断
                break;
            }
        }
        //当线程被中断的时候
        if(!running.get() || Thread.currentThread().isInterrupted()){
            for (AbstractScene scene:scenes){
                if (scene.isRunning()){
                    scene.stop();
                    sceneSet.remove(scene);
                }
            }
        }
        running.set(false);
        alive.set(false);
        logger.info(Thread.currentThread()+"退出");
    }

    /**
     * 注册场景
     * 如果天神正在运行中，会将该场景加入准备挂载列表中，在下一次心跳的时候添加
     * 如果天神尚未运行，会直接加入到场景列表中
     * 如果想让God移除场景，请将场景的running设为false
     * @param scene 对应的场景
     */
    void registerScene(AbstractScene scene){
        ensureAlive();
        if (sceneSet.contains(scene)){
            logger.info(scene.getName()+"重复注册");
        }
        readWriteLock.lock();
        try{
            if(!sceneSet.contains(scene)){
                prepareScenes.add(scene);
                sceneSet.add(scene);
            }
        }finally {
            readWriteLock.unlock();
        }
    }

    private void processRegister(){
        if(prepareScenes.isEmpty()){
            return;
        }
        readWriteLock.lock();
        try{
            scenes.addAll(prepareScenes);
            prepareScenes.clear();
        }finally {
            readWriteLock.unlock();
        }
    }

    private void ensureAlive(){
        if (!this.alive.get()){
            throw new IllegalStateException(name+"未被激活");
        }
    }

    public boolean isAlive(){
        return this.alive.get();
    }

    public String getName() {
        return name;
    }

    void setName(String name) {
        this.name = name;
    }

    void setBindThread(Thread thread){
        this.bindThread = thread;
    }

}
